package webflux;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.netty.http.server.HttpServer;

import java.util.HashMap;
import java.util.Map;

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;

public class Server {
    @SneakyThrows
    public static void main(String[] args) {
        createReactorServer();
        System.out.println("enter to exit");
        System.in.read();
    }

    static void createReactorServer() {
        HttpHandler httpHandler = RouterFunctions.toHttpHandler(routerFunction());
        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);

        HttpServer httpServer = HttpServer.create();
        httpServer.port(55936).handle(adapter).bindNow();
    }

    static RouterFunction<ServerResponse> routerFunction() {
        PersonnelHandler personnelHandler = new PersonnelHandler();
        return RouterFunctions.route(
                GET("/personnel/{id}").and(accept(APPLICATION_JSON)), personnelHandler::getPersonnelById
        ).andRoute(
                GET("/personnel").and(accept(APPLICATION_JSON)), personnelHandler::getAllPersonnel
        );
    }

    static class PersonnelHandler {
        final PersonnelService personnelService = new PersonnelService();

        public Mono<ServerResponse> getPersonnelById(ServerRequest request) {
            int id = Integer.valueOf(request.pathVariable("id"));
            // 空值处理
            Mono<ServerResponse> noFound = ServerResponse.notFound().build();
            Mono<Personnel> personnelMono = personnelService.getPersonnelById(id);
            return personnelMono.flatMap(p -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(personnelMono, Object.class))
                    .switchIfEmpty(noFound);
        }

        public Mono<ServerResponse> getAllPersonnel(ServerRequest request) {
            Flux<Personnel> allPersonnel = personnelService.getAllPersonnel();
            return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(allPersonnel, Personnel.class);
        }

        public Mono<ServerResponse> insertPersonnel(ServerRequest request) {
            Mono<Personnel> personnelMono = request.bodyToMono(Personnel.class);
            return ServerResponse.ok().build(personnelService.insertPersonnel(personnelMono));
        }
    }


    static class PersonnelService {
        final Map<Integer, Personnel> personnelMap = new HashMap<>();

        public PersonnelService() {
            personnelMap.put(1, new Personnel("zhangsan", "man", 28));
            personnelMap.put(2, new Personnel("lisi", "women", 28));
        }

        public Mono<Personnel> getPersonnelById(int id) {
            return Mono.justOrEmpty(personnelMap.get(id));
        }

        public Flux<Personnel> getAllPersonnel() {
            return Flux.fromIterable(personnelMap.values());
        }

        public Mono<Void> insertPersonnel(Mono<Personnel> personnel) {
            return personnel.doOnNext(p -> personnelMap.put(personnelMap.size() + 1, p)).thenEmpty(Mono.empty());
        }
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    static class Personnel {
        String name;
        String gender;
        int age;
    }
}
